/** * OpenKM, Open Document Management System (http://www.openkm.com) * Copyright (c) 2006-2011 Paco Avila & Josep Llort * * No bytes were intentionally harmed during the development of this application. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package com.openkm.core; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.Principal; import java.util.HashSet; import java.util.Map; import java.util.Properties; import java.util.Set; import javax.jcr.Credentials; import javax.jcr.SimpleCredentials; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.login.FailedLoginException; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; import org.apache.jackrabbit.core.security.AnonymousPrincipal; import org.apache.jackrabbit.core.security.CredentialsCallback; import org.apache.jackrabbit.core.security.SecurityConstants; import org.apache.jackrabbit.core.security.SystemPrincipal; import org.apache.jackrabbit.core.security.UserPrincipal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author pavila * * JBoss security framewokk (several login modules): * http://wiki.jboss.org/wiki/Wiki.jsp?page=JBossSX * * JBoss UsersRolesLoginModule.java source code: * http://wiki.jboss.org/wiki/Wiki.jsp?page=JBossSX */ @SuppressWarnings("deprecation") public class OKMLoginModule implements LoginModule { private static Logger log = LoggerFactory.getLogger(OKMLoginModule.class); private String usersRsrcName = "users.properties"; private String rolesRsrcName = "roles.properties"; private Properties users; private Properties roles; private Subject subject; private CallbackHandler callbackHandler; private final Set<Principal> principals = new HashSet<Principal>(); private String defaultUserId = null; @Override public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) { log.debug("initialize("+subject+", "+callbackHandler+", "+sharedState+", "+options+")"); this.subject = subject; this.callbackHandler = callbackHandler; try { this.users = loadProperties(usersRsrcName); this.roles = loadProperties(rolesRsrcName); } catch (Exception e) { // Note that although this exception isn't passed on, users or roles will be null // so that any call to login will throw a LoginException. log.error("Failed to load users/passwords/role files", e); } log.debug("initialize: void"); } @Override public boolean login() throws LoginException { log.debug("login()"); boolean ok; // prompt for a user name and password if (callbackHandler == null) { throw new LoginException("no CallbackHandler available"); } if (users == null) throw new LoginException("Missing users.properties file."); if (roles == null) throw new LoginException("Missing roles.properties file."); boolean authenticated = false; principals.clear(); try { // Get credentials using a JAAS callback CredentialsCallback ccb = new CredentialsCallback(); callbackHandler.handle(new Callback[] { ccb }); Credentials creds = ccb.getCredentials(); // Use the credentials to set up principals if (creds != null) { if (creds instanceof SimpleCredentials) { SimpleCredentials sc = (SimpleCredentials) creds; // authenticate Object attr = sc.getAttribute(SecurityConstants.IMPERSONATOR_ATTRIBUTE); if (attr != null && attr instanceof Subject) { Subject impersonator = (Subject) attr; // @todo check privileges to 'impersonate' the user represented by the supplied credentials log.debug("***** RARO ******"); log.debug(impersonator.toString()); log.debug("***** RARO ******"); } else { // @todo implement simple username/password authentication log.debug("***********"); log.debug(sc.getUserID()+" -> "+new String(sc.getPassword())); log.debug("***********"); if (users.getProperty(sc.getUserID()).equals(new String(sc.getPassword()))) { log.debug("*********** BIEN"); authenticated = true; } else { log.debug("*********** MAL"); authenticated = false; } } if ("anonymousUserId".equals(sc.getUserID())) { principals.add(new AnonymousPrincipal()); authenticated = true; } else { // else assume the user we authenticated is the UserPrincipal principals.add(new UserPrincipal(sc.getUserID())); //java.security.acl.; authenticated = true; } } } else if (defaultUserId != null) { //principals.add(new UserPrincipal(defaultUserId)); principals.add(new SystemPrincipal()); authenticated = true; } else { principals.add(new AnonymousPrincipal()); authenticated = true; } } catch (java.io.IOException ioe) { throw new LoginException(ioe.toString()); } catch (UnsupportedCallbackException uce) { throw new LoginException(uce.getCallback().toString() + " not available"); } if (authenticated) { ok = !principals.isEmpty(); } else { // authentication failed: clean out state principals.clear(); throw new FailedLoginException(); } log.debug("login: "+ok); return ok; } @Override public boolean commit() throws LoginException { log.debug("commit()"); boolean ok; if (principals.isEmpty()) { ok = false; } else { // add a principals (authenticated identities) to the Subject subject.getPrincipals().addAll(principals); ok = true; } log.debug("commit: "+ok); return ok; } @Override public boolean abort() throws LoginException { log.debug("abort()"); boolean ok; if (principals.isEmpty()) { ok = false; } else { logout(); ok = true; } log.debug("abort: "+ok); return ok; } @Override public boolean logout() throws LoginException { log.debug("logout()"); boolean ok = true; subject.getPrincipals().removeAll(principals); principals.clear(); log.debug("logout: "+ok); return ok; } /** * Utility method which loads the given properties file and returns a * Properties object containing the key,value pairs in that file. * The properties files should be in the class path as this method looks * to the thread context class loader (TCL) to locate the resource. If the * TCL is a URLClassLoader the findResource(String) method is first tried. * If this fails or the TCL is not a URLClassLoader getResource(String) is * tried. * @param propertiesName - the name of the properties file resource * @param log - the logger used for trace level messages * @return the loaded properties file if found * @exception IOException thrown if the properties file cannot be found * or loaded */ @SuppressWarnings("unused") static Properties loadProperties(String propertiesName) throws IOException { log.debug("loadProperties("+propertiesName+")"); Properties bundle = null; InputStream is = new FileInputStream(propertiesName); if (is != null) { bundle = new Properties(); bundle.load(is); } else { throw new IOException("Properties file " + propertiesName + " not avilable"); } log.debug("loadProperties: "+bundle); return bundle; } }